﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Linq.Expressions;
using LCA.Common.Util;

public static class EnumerableExtensions
{
    public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool isAscending)
    {
        return isAscending ? source.OrderBy(keySelector) : source.OrderByDescending(keySelector);
    }

    public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, bool isAscending)
    {
        return isAscending ? source.ThenBy(keySelector) : source.ThenByDescending(keySelector);
    }

    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, object> keyExtractor)
    {
        return list.Distinct(new KeyEqualityComparer<T>(keyExtractor));
    }

    public static bool Contains<T>(this IEnumerable<T> list, T item, Func<T, object> keyExtractor)
    {
        return list.Contains(item, new KeyEqualityComparer<T>(keyExtractor));
    }

    public static IEnumerable<T> Except<T>(this IEnumerable<T> list, IEnumerable<T> except, Func<T, object> keyExtractor)
    {
        return list.Except(except, new KeyEqualityComparer<T>(keyExtractor));
    }

    public static IEnumerable<T> Intersect<T>(this IEnumerable<T> list, IEnumerable<T> toIntersect, Func<T, object> keyExtractor)
    {
        return list.Intersect(toIntersect, new KeyEqualityComparer<T>(keyExtractor));
    }

    public static IOrderedEnumerable<T> OrderBy<T, TKey>(this IEnumerable<T> list, Func<T, TKey> keySelector, Func<TKey, TKey, int> comparer)
    {
        return list.OrderBy(keySelector, new KeyComparer<TKey>(comparer));
    }

    public static IOrderedEnumerable<T> OrderByDescending<T, TKey>(this IEnumerable<T> list, Func<T, TKey> keySelector, Func<TKey, TKey, int> comparer)
    {
        return list.OrderByDescending(keySelector, new KeyComparer<TKey>(comparer));
    }

    public static bool SequenceEqual<T>(this IEnumerable<T> list, IEnumerable<T> sequenceToEqual, Func<T, object> keyExtractor)
    {
        return list.SequenceEqual(sequenceToEqual, new KeyEqualityComparer<T>(keyExtractor));
    }

    public static IEnumerable<TSource> Union<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, Func<TSource, object> keyExtractor)
    {
        return first.Union(second, new KeyEqualityComparer<TSource>(keyExtractor));
    }

    public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> list, Func<TSource, TKey> keySelector, Func<TKey, TKey, bool> comparer)
    {
        return list.GroupBy(keySelector, new KeyEqualityComparer<TKey>(comparer));
    }

    public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> list, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, TKey, bool> comparer)
    {
        return list.GroupBy(keySelector, elementSelector, new KeyEqualityComparer<TKey>(comparer));
    }

    public static IEnumerable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IEnumerable<TSource> list, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, Func<TKey, IEnumerable<TElement>, TResult> resultSelector, Func<TKey, TKey, bool> comparer)
    {
        return list.GroupBy(keySelector, elementSelector, resultSelector, new KeyEqualityComparer<TKey>(comparer));
    }

    public static IEnumerable<T> ToEnumerable<T>(this T o)
    {
        return Enumerable.Repeat(o, 1);
    }

    private static Type[] SupportedDataTypes = new Type[] { typeof(Boolean), typeof(Byte), typeof(Char), typeof(DateTime), typeof(Decimal), typeof(Double), typeof(Guid), typeof(Int16), typeof(Int32), typeof(Int64), typeof(SByte), typeof(Single), typeof(String), typeof(TimeSpan), typeof(UInt16), typeof(UInt32), typeof(UInt64) };
    public static DataTable ToDataTable<T>(this IEnumerable<T> list)
    {
        DataTable table = new DataTable();

        foreach (T value in list)
        {
            if (table.Columns.Count == 0)
            {
                foreach (var p in value.GetType().GetProperties())
                {
                    var dataType = p.PropertyType;
                    if (Nullable.GetUnderlyingType(dataType) != null)
                    {
                        dataType = Nullable.GetUnderlyingType(dataType);
                    }
                    if (!SupportedDataTypes.Contains(dataType))
                    {
                        dataType = typeof(string);
                    }

                    table.Columns.Add(p.Name, dataType);
                }
            }

            DataRow dr = table.NewRow();
            foreach (var p in value.GetType().GetProperties())
            {
                var data = p.GetValue(value, null);
                dr[p.Name] = data == null ? DBNull.Value : p.GetValue(value, null);

            }
            table.Rows.Add(dr);
        }

        return table;
    }
}